package cn.lingyangwl.agile.infra.weixin.mp.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.lingyangwl.agile.common.core.exception.enums.GlobalErrorEnum;
import cn.lingyangwl.agile.infra.InfraApi;
import cn.lingyangwl.agile.infra.module.file.model.rqrs.FileRecordResp;
import cn.lingyangwl.agile.infra.weixin.mp.assembly.MpMaterialAssembly;
import cn.lingyangwl.agile.infra.weixin.mp.manager.MpMaterialManager;
import cn.lingyangwl.agile.infra.weixin.mp.mapper.MpMaterialMapper;
import cn.lingyangwl.agile.infra.weixin.mp.model.WxMpContext;
import cn.lingyangwl.agile.infra.weixin.mp.model.entity.MpMaterial;
import cn.lingyangwl.agile.infra.weixin.mp.model.rqrs.MpMaterialPageReq;
import cn.lingyangwl.agile.infra.weixin.mp.model.rqrs.MpMaterialResp;
import cn.lingyangwl.agile.infra.weixin.mp.model.rqrs.MpMaterialSaveReq;
import cn.lingyangwl.agile.infra.weixin.mp.model.rqrs.MpMaterialUpdateReq;
import cn.lingyangwl.agile.infra.weixin.mp.handler.WxMpHandler;
import cn.lingyangwl.agile.infra.weixin.mp.service.MpMaterialService;
import cn.lingyangwl.framework.mybatis.plus.utils.PageUtil;
import cn.lingyangwl.framework.tool.core.exception.BizException;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.bean.material.WxMpMaterialUploadResult;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 公众号素材
 *
 * @author shenguangyang
 */
@Slf4j
@Service
public class MpMaterialServiceImpl extends ServiceImpl<MpMaterialMapper, MpMaterial>
    implements IService<MpMaterial>, MpMaterialService {

    @Resource
    private MpMaterialManager mpMaterialManager;
    @Resource
    private MpMaterialAssembly mpMaterialAssembly;
    @Resource
    private WxMpHandler wxMpHandler;

    @Resource
    private InfraApi infraApi;

    @Override
    public MpMaterial savePermanentMaterial(MpMaterialSaveReq req) throws IOException {
        // 1. 上传到公众号
        WxMpContext context = wxMpHandler.getContext();
        me.chanjar.weixin.mp.api.WxMpService mpService = context.getService();

        WxMpMaterialUploadResult result;
        File file = null;
        String name;
        FileRecordResp fileRecord;
        try {
            // 1.1 从文件服务中拉取文件
            fileRecord = infraApi.getFileRecordByPath(req.getFilePath(), true);
            name = StrUtil.blankToDefault(req.getName(), fileRecord.getOriginalName());

            // 写入到临时文件
            file = FileUtil.newFile(FileUtil.getTmpDirPath() + fileRecord.getNewName());
            HttpUtil.downloadFile(fileRecord.getFileUrl(), file);

            // 1.2 上传到公众号
            result = mpService.getMaterialService().materialFileUpload(req.getType(),
                    mpMaterialAssembly.toWxMpMaterial(name, file, req.getTitle(), req.getIntroduction()));
        } catch (WxErrorException e) {
            throw new BizException(e.getError().getErrorMsg());
        } finally {
            FileUtil.del(file);
        }

        // 2. 存储到数据库
        MpMaterial material = mpMaterialAssembly.toMpMaterial(result.getMediaId(), req.getType(), req.getFilePath(),
                name, req.getTitle(), req.getIntroduction(), result.getUrl());
        material.setPermanent(true);
        material.setMpAppId(wxMpHandler.getContext().getMpParam().getAppId());

        mpMaterialManager.save(material);
        return material;
    }

    /**
     * 分页查询 公众号素材
     *
     * @return 返回分页数据
     */
    @Override
    public IPage<MpMaterialResp> pageMaterial(MpMaterialPageReq req) {
        LocalDateTime beginTime = req.getBeginLocalDateTime();
        LocalDateTime endTime = req.getEndLocalDateTime();
        LambdaQueryWrapper<MpMaterial> queryWrapper = MpMaterial.lqw()
            .eq(StrUtil.isNotEmpty(req.getType()), MpMaterial::getType, req.getType())
            .ge(Objects.nonNull(beginTime), MpMaterial::getCreatedAt, beginTime)
            .le(Objects.nonNull(endTime), MpMaterial::getCreatedAt, endTime)
            .orderByDesc(MpMaterial::getCreatedAt);
        IPage<MpMaterial> pageData = this.page(PageUtil.to(req), queryWrapper);
        return  PageUtil.to(pageData, mpMaterialAssembly::toMpMaterialResp);
    }
    
    /**
     * 查询 公众号素材 所有记录
     *
     * @return 返回集合，没有返回空List
     */
    @Override
    public List<MpMaterialResp> listMaterial() {
    	return this.list().stream()
    	        .map(mpMaterialAssembly::toMpMaterialResp).collect(Collectors.toList());
    }


    /**
     * 根据主键查询 公众号素材
     *
     * @param id 主键
     * @return 返回记录，没有返回null
     */
    @Override
    public MpMaterialResp getMaterialById(Long id) {
        MpMaterial entity = this.getById(id);
    	return mpMaterialAssembly.toMpMaterialResp(entity);
    }

    /**
     * 更新 公众号素材
     *
     * @param req 修改的记录
     * @return 返回影响行数
     */
    @Override
    public boolean updateMaterialById(MpMaterialUpdateReq req) {
        MpMaterial entity = mpMaterialAssembly.toMpMaterial(req);
    	return this.updateById(entity);
    }

    /**
     * 通过id删除 公众号素材
     *
     * @param id 删除的主键
     * @return 返回影响行数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteMaterialById(Long id) {
        MpMaterial dbMaterial = this.getById(id);
        if (Objects.isNull(dbMaterial)) {
            throw new BizException(GlobalErrorEnum.OPERATION_FAIL_SELETE);
        }
        boolean ret = this.removeById(id);
        if (dbMaterial.getPermanent()) {
            WxMpContext context = wxMpHandler.getContext();
            try {
                context.getService().getMaterialService().materialDelete(dbMaterial.getMediaId());
            } catch (WxErrorException e) {
                log.error("errorMsg: {}", e.getError().getErrorMsg());
                throw new BizException(e.getError().getErrorMsg());
            }

            // 删除文件服务器上的文件数据
            infraApi.deleteFileRecordByPath(dbMaterial.getUrl());
        }
        return ret;
    }
}
